﻿namespace NTLib.Core.Shared
{
    using NTLib.Core.Threading;
    using System;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;

    /// <summary>
    /// Define a container used to shared values. Only one stored is the shared value is used, otherwise a value is stored by type.
    /// </summary>
    public static class StaticSharedContainer
    {
        #region Fields

        private static readonly SemaphoreLocker s_locker;
        private static readonly Dictionary<Type, Dictionary<Type, Object>> s_resources;

        #endregion

        #region Ctor

        /// <summary>
        /// Initializes the <see cref="StaticSharedContainer"/> class.
        /// </summary>
        static StaticSharedContainer()
        {
            s_locker = new SemaphoreLocker();
            s_resources = new Dictionary<Type, Dictionary<Type, Object>>();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets a static resources indexed by calling type.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static T GetStaticResources<T>(this object th)
            where T : new()
        {
            return GetStaticResources<T>(th.GetType());
        }

        /// <summary>
        /// Gets a static resources indexed.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static T GetStaticResources<T>(this object th, Type typeKey)
            where T : new()
        {
            return GetStaticResources<T>(typeKey);
        }


        /// <summary>
        /// Gets a static resources indexed type.
        /// </summary>
        public static T GetStaticResources<T>(Type typeKey)
            where T : new()
        {
            object obj = null;
            using (s_locker.ScopeLock())
            {
                Dictionary<Type, Object> objDic = null;
                if (s_resources.TryGetValue(typeKey, out objDic))
                    objDic.TryGetValue(typeof(T), out obj);
                else
                    s_resources.Add(typeKey, new Dictionary<Type, object>());

                if (obj == null)
                {
                    obj = new T();
                    s_resources[typeKey].Add(typeof(T), obj);
                }
                return (T)obj;
            }
        }

        #endregion
    }
}
